#include <fermat/common/date_time_formatter.h>
#include <fermat/common/date_time_format.h>
#include <fermat/common/timestamp.h>

namespace fermat {


void DateTimeFormatter::append(std::string& str, const LocalDateTime& dateTime, const std::string& fmt)
{
	DateTimeFormatter::append(str, dateTime._dateTime, fmt, dateTime.tzd());
}


void DateTimeFormatter::append(std::string& str, const DateTime& dateTime, const std::string& fmt, int timeZoneDifferential)
{
	std::string::const_iterator it  = fmt.begin();
	std::string::const_iterator end = fmt.end();
	fermat::MemoryWriter out;
	std::string word;
	while (it != end) {
		if (*it == '%')  {
			if (++it != end) {
				switch (*it) {
					case 'w': out.write("{}", DateTimeFormat::WEEKDAY_SHORT_NAMES[dateTime.dayOfWeek()]); break;
					case 'W': out.write("{}", DateTimeFormat::WEEKDAY_NAMES[dateTime.dayOfWeek()]); break;
					case 'b': out.write("{}", DateTimeFormat::MONTH_SHORT_NAMES[dateTime.month() - 1]); break;
					case 'B': out.write("{}", DateTimeFormat::MONTH_NAMES[dateTime.month() - 1]); break;
					case 'd': out.write("{:02}", dateTime.day()); break;
					case 'e': out.write("{}", dateTime.day()); break;
					case 'f': out.write("{:02}", dateTime.day()); break;
					case 'm': out.write("{:02}", dateTime.month(), 2); break;
					case 'n': out.write("{}", dateTime.month()); break;
					case 'o': out.write("{:02}", dateTime.month()); break;
					case 'y': out.write("{:02}", dateTime.year() % 100); break;
					case 'Y': out.write("{:04}", dateTime.year()); break;
					case 'H': out.write("{:02}", dateTime.hour()); break;
					case 'h': out.write("{:02}", dateTime.hourAMPM()); break;
					case 'a': out.write("{}",dateTime.isAM() ? "am" : "pm"); break;
					case 'A': out.write("{}",dateTime.isAM() ? "AM" : "PM"); break;
					case 'M': out.write("{:02}", dateTime.minute()); break;
					case 'S': out.write("{:02}", dateTime.second()); break;
					case 's': out.write("{:02}.{:06}", dateTime.second(), dateTime.millisecond()*1000 + dateTime.microsecond()); 
							break;
					case 'i': out.write("{:03}", dateTime.millisecond()); break;
					case 'c': out.write("{}", dateTime.millisecond()/100); break;
					case 'F': out.write("{:06}", dateTime.millisecond()*1000 + dateTime.microsecond()); break;
					case 'z': tzdISO(out, timeZoneDifferential);break;
					case 'Z': tzdRFC(out, timeZoneDifferential);break;
					default:  out<<*it;
				}
				++it;
			}
		}
		else out<<*it++;
	}
	str.append(out.data(),out.size());
}


void DateTimeFormatter::append(std::string& str, const Timespan& timespan, const std::string& fmt)
{
	std::string::const_iterator it  = fmt.begin();
	std::string::const_iterator end = fmt.end();
	fermat::MemoryWriter out;
	while (it != end)
	{
		if (*it == '%')
		{
			if (++it != end)
			{
				switch (*it)
				{
				case 'd': out.write("{}", timespan.days()); break;
				case 'H': out.write("{:02}", timespan.hours()); break;
				case 'h': out.write("{}", timespan.totalHours()); break;
				case 'M': out.write("{:02}", timespan.minutes()); break;
				case 'm': out.write("{}", timespan.totalMinutes()); break;
				case 'S': out.write("{}", timespan.seconds()); break;
				case 's': out.write("{}", timespan.totalSeconds()); break;
				case 'i': out.write("{:03}", timespan.milliseconds()); break;
				case 'c': out.write("{}", timespan.milliseconds()/100); break;
				case 'F': out.write("{:06}", timespan.milliseconds()*1000 + timespan.microseconds()); break;
				default:  out<<*it;
				}
				++it;
			}
		}
		else out<<*it++;
	}
	str.append(out.data(), out.size());
}


void DateTimeFormatter::tzdISO(fermat::MemoryWriter& out, int timeZoneDifferential)
{
	if (timeZoneDifferential != UTC)
	{
		if (timeZoneDifferential >= 0)
		{
			out.write("+{:02}:{:02}", timeZoneDifferential/3600, (timeZoneDifferential%3600)/60);
		}
		else
		{
			out.write("-{:02}:{:02}", -timeZoneDifferential/3600, (-timeZoneDifferential%3600)/60);
		}
	}
	else out<<'Z';
}


void DateTimeFormatter::tzdRFC(fermat::MemoryWriter& out,  int timeZoneDifferential)
{
	if (timeZoneDifferential != UTC)
	{
		if (timeZoneDifferential >= 0)
		{
			out.write("+{:02}{:02}", timeZoneDifferential/3600,(timeZoneDifferential%3600)/60);
		}
		else
		{
			out.write("-{:02}{:02}", -timeZoneDifferential/3600,(-timeZoneDifferential%3600)/60);
		}		
	}
	else out<<"GMT";
}


} // namespace Poco
